using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Text;
using Forecast.Database;

namespace Forecast.Business
{
    public class TradersOverview
    {
        #region Enums

        public enum Columns
        {
            None,
            PlayerId,
            UserName,
            ActivityLevel,
            LastActivity,
            FavouriteStock,
            AvailableBalance,
            ActualBalance,
            StockValue,
            TotalValue,
            Medals,
            MedalsInPredictions
        }

        #endregion  
        #region Variables

        private Hashtable _hashtablePredictionValues;

        #endregion
        #region Interface

        public DataTable GetOverview(IList<Prediction> allActivePredictions, IList<Prediction> allClosedPredictions, IList<Player> allPlayers, IList<Offer> allActiveOffers, IList<Holding> allHoldings, IList<Medal> allMedals, IList<PredictionResult> allPredictionResults, IList<Offer> allOffers, IList<NewsItem> allNewsItems)
        {
            DataTable table = GetEmptyDataTableForPlayerOverview();
            foreach (Player player in allPlayers)
            {
                DataRow row = table.NewRow();
                row[Columns.PlayerId.ToString()] = player.Id;
                row[Columns.UserName.ToString()] = player.UserName;
                row[Columns.ActivityLevel.ToString()] = GetActivityLevel(player, allOffers);
                row[Columns.LastActivity.ToString()] = GetLastActivity(player, allOffers);
                row[Columns.FavouriteStock.ToString()] = GetFavouriteStock(player, allActivePredictions, allHoldings, allActiveOffers);
                row[Columns.AvailableBalance.ToString()] = player.Cash;
                int cashInStocks = GetCashInStocks(player, allActiveOffers);
                row[Columns.ActualBalance.ToString()] = cashInStocks + player.Cash;
                int stockValue = GetTotalValueOfStocksForPlayer(player, allActivePredictions, allHoldings, allActiveOffers);
                row[Columns.StockValue.ToString()] = stockValue;
                row[Columns.TotalValue.ToString()] = player.Cash + stockValue + cashInStocks;
                MedalsForPlayer medalsForPlayer = GetMedalsForPlayer(player, allPredictionResults, allClosedPredictions);
                row[Columns.Medals.ToString()] = medalsForPlayer.MedalIds;
                row[Columns.MedalsInPredictions.ToString()] = medalsForPlayer.MedalInPredictions;

                table.Rows.Add(row);
            }

            return table;
        }

        private string GetFavouriteStock(Player player, IList<Prediction> allActivePredictions, IList<Holding> allHoldings, IList<Offer> allActiveOffers)
        {
            FavouriteStock favouriteStock = new FavouriteStock(player, allActivePredictions);
            return favouriteStock.GetFavourite(allHoldings, allActiveOffers);
        }

        private int GetStocksInOffers(Player player, IList<Offer> allActiveOffers, int predictionId)
        {
            int numStocks = 0;

            foreach (Offer offer in allActiveOffers)
            {
                if (offer.PlayerId != player.Id)
                    continue;
                if (offer.PredictionId != predictionId)
                    continue;
                if (offer.IsBuy)
                    continue;
                numStocks += offer.NumPredictions;
            }

            return numStocks;
        }

        private DateTime GetLastActivity(Player player, IList<Offer> allOffers)
        {
            DateTime date = DateTime.MinValue;

            foreach (Offer offer in allOffers)
            {
                if (!offer.IsAccepted)
                    continue;
                if (offer.AcceptorId != player.Id)
                    continue;

                if (offer.EndDate > date)
                    date = offer.EndDate;
            }

            return date;
        }

        private int GetActivityLevel(Player player, IList<Offer> allOffers)
        {
            int activityLevel = 0;

            foreach (Offer offer in allOffers)
            {
                if (offer.PlayerId != player.Id)
                {
                    if (offer.IsAccepted && offer.AcceptorId == player.Id)
                        activityLevel++;
                    continue;
                }
                activityLevel++;
            }

            return activityLevel;
        }

        #endregion
        #region Helper methods

        internal int GetCashInStocks(Player player, IList<Offer> allActiveOffers)
        {
            int cashInStocks = 0;
            foreach (Offer offer in allActiveOffers)
            {
                if (offer.PlayerId != player.Id)
                    continue;
                if (!offer.IsBuy)
                    continue;
                cashInStocks += offer.Price * offer.NumPredictions;
            }
            return cashInStocks;
        }

        private MedalsForPlayer GetMedalsForPlayer(Player player, IList<PredictionResult> predictionResults, IList<Prediction> predictions)
        {
            ArrayList medals = new ArrayList();
            ArrayList medalsInPredictions = new ArrayList();
            foreach (PredictionResult result in predictionResults)
            {
                if (result.PlayerId != player.Id)
                    continue;
                if (result.MedalId > 1)
                {
                    medals.Add(result.MedalId);
                    medalsInPredictions.Add(GetTicker(result.PredictionId, predictions));
                }
            }

            MedalsForPlayer medalsForPlayer = new MedalsForPlayer();
            if (medals.Count == 0)
                return medalsForPlayer;

            medalsForPlayer.MedalIds = (int[])medals.ToArray(typeof(int));
            medalsForPlayer.MedalInPredictions = (string[])medalsInPredictions.ToArray(typeof(string));
            return medalsForPlayer;
        }

        private string GetTicker(int predictionId, IList<Prediction> predictions)
        {
            foreach (Prediction prediction in predictions)
            {
                if (prediction.Id == predictionId)
                    return prediction.Ticker;
            }
            return "Ticker not found";
        }

        private int GetPredictionValue(int predictionId, IList<Prediction> allActivePredictions)
        {
            if (_hashtablePredictionValues == null || _hashtablePredictionValues.Count < allActivePredictions.Count)
                InitStockValues(allActivePredictions);
            object predictionValue = _hashtablePredictionValues[predictionId];
            if (predictionValue == null)
                return 0;
            return (int)predictionValue;
        }

        internal int GetTotalValueOfStocksForPlayer(Player player, IList<Prediction> predictions, IList<Holding> holdings, IList<Offer> offers)
        {
            int totalValueOfStocks = 0;

            foreach (Holding holding in holdings)
            {
                if (holding.PlayerId != player.Id)
                    continue;
                int predictionValue = GetPredictionValue(holding.PredictionId, predictions);
                totalValueOfStocks += holding.NumPredictions * predictionValue;
            }

            foreach (Offer offer in offers)
            {
                if (!offer.IsBuy)
                {
                    if (offer.PlayerId != player.Id)
                        continue;                    

                    int predictionValue = GetPredictionValue(offer.PredictionId, predictions);
                    if (predictionValue > offer.Price)
                        predictionValue = offer.Price;
                    totalValueOfStocks += predictionValue * offer.NumPredictions;                    
                }
            }
            return totalValueOfStocks;
        }

        private void InitStockValues(IList<Prediction> allPredictions)
        {            
            _hashtablePredictionValues = new Hashtable();
            foreach (Prediction prediction in allPredictions)
                _hashtablePredictionValues.Add(prediction.Id, prediction.LastValue);         
        }

        private DataTable GetEmptyDataTableForPlayerOverview()
        {
            DataTable dataTable = new DataTable("PlayerOverview");

            DataColumn columnPlayerId = new DataColumn();
            columnPlayerId.ColumnName = Columns.PlayerId.ToString();
            columnPlayerId.DataType = typeof(int);
            dataTable.Columns.Add(columnPlayerId);

            DataColumn columnPlayerName = new DataColumn();
            columnPlayerName.ColumnName = Columns.UserName.ToString();
            columnPlayerName.DataType = typeof(string);
            dataTable.Columns.Add(columnPlayerName);

            DataColumn columnActivityLevel = new DataColumn();
            columnActivityLevel.ColumnName = Columns.ActivityLevel.ToString();
            columnActivityLevel.DataType = typeof(int);
            dataTable.Columns.Add(columnActivityLevel);

            DataColumn columnLastActivity = new DataColumn();
            columnLastActivity.ColumnName = Columns.LastActivity.ToString();
            columnLastActivity.DataType = typeof(DateTime);
            dataTable.Columns.Add(columnLastActivity);

            DataColumn columnFavouriteStock = new DataColumn();
            columnFavouriteStock.ColumnName = Columns.FavouriteStock.ToString();
            columnFavouriteStock.DataType = typeof(string);
            dataTable.Columns.Add(columnFavouriteStock);

            DataColumn columnCash = new DataColumn();
            columnCash.ColumnName = Columns.AvailableBalance.ToString();
            columnCash.DataType = typeof(int);
            dataTable.Columns.Add(columnCash);

            DataColumn columnCashInOffers = new DataColumn();
            columnCashInOffers.ColumnName = Columns.ActualBalance.ToString();
            columnCashInOffers.DataType = typeof(int);
            dataTable.Columns.Add(columnCashInOffers);

            DataColumn columnStockValue = new DataColumn();
            columnStockValue.ColumnName = Columns.StockValue.ToString();
            columnStockValue.DataType = typeof(int);
            dataTable.Columns.Add(columnStockValue);

            DataColumn columnTotalValue = new DataColumn();
            columnTotalValue.ColumnName = Columns.TotalValue.ToString();
            columnTotalValue.DataType = typeof(int);
            dataTable.Columns.Add(columnTotalValue);

            DataColumn columnMedals = new DataColumn();
            columnMedals.ColumnName = Columns.Medals.ToString();
            columnMedals.DataType = typeof(int[]);
            dataTable.Columns.Add(columnMedals);

            DataColumn columnMedalsInPredictions = new DataColumn();
            columnMedalsInPredictions.ColumnName = Columns.MedalsInPredictions.ToString();
            columnMedalsInPredictions.DataType = typeof(string[]);
            dataTable.Columns.Add(columnMedalsInPredictions);

            return dataTable;
        }

        #endregion
    }
}
